home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 2: CDPD 1
/
Almathera Ten on Ten - Disc 2: CDPD 1.iso
/
pd
/
201-225
/
225
/
amigatcp
/
src
/
smtpserv.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-13
|
9KB
|
415 lines
/* SMTP Server state machine - see RFC 821
* Very simple implementation; no forwarding allowed
* (who wants to re-create "sendmail" ??)
*/
#include <stdio.h>
#include "machdep.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"
#include "smtp.h"
#ifdef LATTICE
#define tmpfile(x) tmpnam("testingxxxxxxxxxx")
#endif
/* Command table */
static char *commands[] = {
"helo",
#define HELO_CMD 0
"noop",
#define NOOP_CMD 1
"mail from:",
#define MAIL_CMD 2
"quit",
#define QUIT_CMD 3
"rcpt to:",
#define RCPT_CMD 4
"help",
#define HELP_CMD 5
"data",
#define DATA_CMD 6
"rset",
#define RSET_CMD 7
NULLCHAR
};
/* Reply messages */
static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
static char banner[] = "220 %s SMTP Ready\r\n";
static char closing[] = "221 Closing\r\n";
static char ok[] = "250 Ok\r\n";
static char reset[] = "250 Reset state\r\n";
static char sent[] = "250 Sent\r\n";
static char ourname[] = "250 %s, Share and Enjoy!\r\n";
static char enter[] = "354 Enter mail, end with .\r\n";
static char ioerr[] = "452 Temp file write error\r\n";
static char mboxerr[] = "452 Mailbox %s write error\r\n";
static char badcmd[] = "500 Command unrecognized\r\n";
static char syntax[] = "501 Syntax error\r\n";
static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
static char badname[] = "550 Can't open mailbox for %s\r\n";
static struct tcb *smtp_tcb;
/* Start up SMTP receiver service */
smtp_start(argc,argv)
int argc;
char *argv[];
{
struct socket lsocket;
void r_mail(),s_mail();
lsocket.address = ip_addr;
if(argc < 2)
lsocket.port = SMTP_PORT;
else
lsocket.port = atoi(argv[1]);
smtp_tcb = open_tcp(&lsocket,NULLSOCK,
TCP_PASSIVE,0,r_mail,NULLVFP,s_mail,0,(int *)NULL);
}
/* Shutdown SMTP service (existing connections are allowed to finish) */
smtp_stop()
{
if(smtp_tcb != NULLTCB)
close_tcp(smtp_tcb);
}
/* SMTP connection state change upcall handler */
static void
s_mail(tcb,old,new)
struct tcb *tcb;
char old,new;
{
struct mail *mp,*mail_create();
switch(new){
#ifdef QUICKSTART
case SYN_RECEIVED:
#else
case ESTABLISHED:
#endif
if((mp = mail_create(tcb)) == NULLMAIL){
close_tcp(tcb);
break;
}
tprintf(mp->tcb,banner,hostname);
log(tcb,"open SMTP");
break;
case CLOSE_WAIT:
mp = (struct mail *)tcb->user;
mail_delete(mp);
close_tcp(tcb);
break;
case CLOSED:
log(tcb,"close SMTP");
del_tcp(tcb);
/* Check if server is being shut down */
if(tcb == smtp_tcb)
smtp_tcb = NULLTCB;
break;
}
}
/* SMTP receiver upcall handler */
static void
r_mail(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
register struct mail *mp;
char *index(),*inet_ntoa(),c;
struct mbuf *bp;
void docommand(),deliver(),doline();
if((mp = (struct mail *)tcb->user) == NULLMAIL){
/* Unknown session */
close_tcp(tcb);
return;
}
recv_tcp(tcb,&bp,cnt);
/* Assemble an input line in the session buffer.
* Return if incomplete
*/
while(pullup(&bp,&c,1) == 1){
switch(c){
case '\r': /* Strip cr's */
continue;
case '\n': /* Complete line; process it */
mp->buf[mp->cnt] = '\0';
doline(mp);
break;
default: /* Assemble line */
mp->buf[mp->cnt++] = c;
break;
}
}
}
/* Process a line read on an SMTP connection (any state) */
static
void
doline(mp)
register struct mail *mp;
{
switch(mp->state){
case COMMAND_STATE:
docommand(mp);
break;
case DATA_STATE:
tcp_output(mp->tcb); /* Send ACK; disk I/O is slow */
if(mp->buf[0] == '.' && strlen(mp->buf) == 1){
fprintf(mp->data,"\n"); /* Leave a blank line between msgs */
mp->state = COMMAND_STATE;
deliver(mp); /* Also sends appropriate response */
break;
}
/* Append to data file */
if(fprintf(mp->data,"%s\n",mp->buf) < 0){
mp->state = COMMAND_STATE;
tprintf(mp->tcb,ioerr);
}
break;
}
mp->cnt = 0;
}
/* Create control block, initialize */
static struct mail *
mail_create(tcb)
register struct tcb *tcb;
{
register struct mail *mp;
char *calloc();
if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL){
return NULLMAIL;
}
mp->tcb = tcb; /* Downward pointer */
tcb->user = (int *)mp; /* Upward pointer */
return mp;
}
/* Free resources, delete control block */
static
mail_delete(mp)
register struct mail *mp;
{
register struct addr *ap,*ap1;
if(mp->system != NULLCHAR)
free(mp->system);
for(ap = mp->to;ap != NULLADDR;ap = ap1){
if(ap->val != NULLCHAR)
free(ap->val);
ap1 = ap->next;
free((char *)ap);
}
if(mp->data != NULLFILE)
fclose(mp->data);
free((char *)mp);
}
/* Parse and execute mail commands */
static
void
docommand(mp)
register struct mail *mp;
{
char mailbox[50];
char *cmd,*arg,*cp,*cp1,**cmdp;
char *index(),*malloc(),*getname();
struct tcb *tcb;
struct addr *ap;
#ifdef LATTICE
FILE *fp;
#else
FILE *tmpfile(),*fp;
#endif
#ifdef DATE
long t;
char *ctime();
#endif
cmd = mp->buf;
if(mp->cnt < 4){
/* Can't be a legal SMTP command */
tprintf(mp->tcb,badcmd);
return;
}
cmd = mp->buf;
/* Translate entire buffer to lower case */
for(cp = cmd;*cp != '\0';cp++)
*cp = tolower(*cp);
/* Find command in table; if not present, return syntax error */
for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
break;
if(*cmdp == NULLCHAR){
tprintf(mp->tcb,badcmd);
return;
}
arg = &cmd[strlen(*cmdp)];
/* Skip spaces after command */
while(*arg == ' ')
*arg++;
/* Execute specific command */
switch(cmdp-commands){
case HELO_CMD:
if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
/* If the system is out of memory, just close */
close_tcp(mp->tcb);
mail_delete(mp);
break;
} else {
strcpy(mp->system,arg);
tprintf(mp->tcb,ourname,hostname);
}
break;
case NOOP_CMD:
case MAIL_CMD: /* Rather useless */
tprintf(mp->tcb,ok);
break;
case QUIT_CMD:
tprintf(mp->tcb,closing);
close_tcp(mp->tcb);
mail_delete(mp);
break;
case RCPT_CMD: /* Specify recipient */
if((cp = getname(arg)) == NULLCHAR){
tprintf(mp->tcb,syntax);
break;
}
/* Strip the @host part; all names must be local */
if((cp1 = index(cp,'@')) != NULLCHAR)
*cp1 = '\0';
/* Check to see if we can open the mailbox */
sprintf(mailbox,MAILSPOOL,cp);
#ifdef AMIGA
/* probably could use AMIGA's Lock()/Unlock(), but that
seems like overkill */
if((fp = fopen(mailbox,"r")) == NULL){
#else
if((fp = fopen(mailbox,"a+")) == NULL){
#endif
tprintf(mp->tcb,badname,cp);
break;
}
fclose(fp);
/* Allocate an entry on the recipient list. This
* assembles the list backwards, but what the heck.
*/
if((ap = (struct addr *)malloc(sizeof(struct addr))) == NULLADDR){
close_tcp(mp->tcb);
mail_delete(mp);
break;
}
if((ap->val = malloc((unsigned)strlen(mailbox)+1)) == NULLCHAR){
free((char *)ap);
close_tcp(mp->tcb);
mail_delete(mp);
break;
}
strcpy(ap->val,mailbox);
ap->next = mp->to;
mp->to = ap;
tprintf(mp->tcb,ok);
break;
case HELP_CMD:
tprintf(mp->tcb,help);
break;
case DATA_CMD:
if(mp->to == NULLADDR){
tprintf(mp->tcb,needrcpt);
break;
}
tcp_output(mp->tcb); /* Send ACK; disk I/O is slow */
if((mp->data = tmpfile()) == NULLFILE){
tprintf(mp->tcb,ioerr);
break;
}
#ifdef DATE
/* Add timestamp; ctime adds newline */
time(&t);
if(mp->system != NULLCHAR)
fprintf(mp->data,
"Received: from %s by %s\n\twith SMTP ; %s",
mp->system,
hostname,
ctime(&t));
else
fprintf(mp->data,"Received: %s\n",ctime(&t));
#endif
if(ferror(mp->data)){
tprintf(mp->tcb,ioerr);
} else {
mp->state = DATA_STATE;
tprintf(mp->tcb,enter);
}
break;
case RSET_CMD:
tcb = mp->tcb;
mail_delete(mp);
if((mp = mail_create(tcb)) == NULLMAIL){
close_tcp(tcb);
} else {
mp->state = COMMAND_STATE;
tprintf(mp->tcb,reset);
}
break;
}
}
/* Given a string of the form <user@host>, extract the part inside the
* brackets and return a pointer to it.
*/
static
char *
getname(cp)
register char *cp;
{
char *cp1;
char *index(),*strncpy();
if((cp = index(cp,'<')) == NULLCHAR){
return NULLCHAR;
}
cp++; /* cp -> first char of name */
if((cp1 = index(cp,'>')) == NULLCHAR){
return NULLCHAR;
}
*cp1 = '\0';
return cp;
}
/* Deliver mail to the appropriate mail boxes and delete temp file */
static
void
deliver(mp)
register struct mail *mp;
{
char *index();
int c;
register struct addr *ap;
register FILE *fp;
for(ap = mp->to;ap != NULLADDR;ap = ap->next){
fseek(mp->data,0L,0); /* rewind */
fp = fopen(ap->val,"a+");
while((c = getc(mp->data)) != EOF){
if(putc(c,fp) == EOF)
break;
}
if(ferror(fp)){
fclose(fp);
tprintf(mp->tcb,mboxerr,ap->val);
fclose(mp->data);
mp->data = NULLFILE;
return;
}
fclose(fp);
}
tprintf(mp->tcb,sent);
fclose(mp->data);
mp->data = NULLFILE;
}